package com.uyuni.rpc.transport.watcher;

import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.util.Timeout;
import io.netty.util.Timer;
import io.netty.util.TimerTask;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.SocketAddress;

import static java.util.concurrent.TimeUnit.MILLISECONDS;

/**
 * @author BazingaLyn
 * @description 监控重连狗
 * @time
 * @modifytime
 */
@ChannelHandler.Sharable
public abstract class ConnectionWatchdog extends ChannelInboundHandlerAdapter implements TimerTask, ChannelHandlerHolder {

    private static final Logger logger = LoggerFactory.getLogger(ConnectionWatchdog.class);

    private final Bootstrap bootstrap;
    private final Timer timer;

    private boolean firstConnection = true;
    private volatile SocketAddress remoteAddress;

    // 是否重连
    private volatile boolean reconnect = true;
    // 尝试重连次数
    private int attempts;

    public ConnectionWatchdog(Bootstrap bootstrap, Timer timer) {
        this.bootstrap = bootstrap;
        this.timer = timer;
    }

    public boolean isReconnect() {
        return reconnect;
    }

    public void setReconnect(boolean reconnect) {
        this.reconnect = reconnect;
    }

    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        Channel channel = ctx.channel();

        attempts = 0;
        firstConnection = true;

        logger.info("Connects with {}.", channel);

        ctx.fireChannelActive();
    }

    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        logger.info("当前channel inactive 将关闭链接");
        boolean doReconnect = reconnect;
        if (doReconnect) {
            if (firstConnection) {
                remoteAddress = ctx.channel().remoteAddress();
                firstConnection = false;
            }
            if (attempts < 12) {
                attempts++;
            }
            // 重连延时，随次数增加而增长
            long timeout = 2 << attempts;
            logger.info("start reconnection...");
            //延时重连 使用定时任务
            timer.newTimeout(this, timeout, MILLISECONDS);
        }

        logger.warn("Disconnects with {}, address: {}, reconnect: {}.", ctx.channel(), remoteAddress, doReconnect);

        ctx.fireChannelInactive();
    }

    @Override
    public void run(Timeout timeout) throws Exception {

        logger.info("reconnecting...");
        ChannelFuture future;
        synchronized (bootstrap) {
            bootstrap.handler(new ChannelInitializer<Channel>() {
                @Override
                protected void initChannel(Channel channel) throws Exception {
                    channel.pipeline().addLast(handlers());
                }
            });
            future = bootstrap.connect(remoteAddress);
        }

        future.addListener((ChannelFutureListener) channelFuture -> {
            boolean succeed = channelFuture.isSuccess();
            logger.warn("reconnects with {}, {}.", remoteAddress, succeed ? "succeed" : "failed");
            if (!succeed) {
                channelFuture.channel().pipeline().fireChannelInactive();
            }
        });
    }

    /**
     * n<<m
     * 使一个整数n左移m位
     * n<<m=n*(2^m)
     * @param args
     */
    public static void main(String[] args) {
        long a = 2L;
        a = a<<2;
        System.out.println(a);
    }

}
